Solidity — Part 3- Inheritance, Virtual, Override, and Super

Shishir Singh
4 min readJun 28, 2023

Solidity is a programming language used to write smart contracts on the Ethereum blockchain. Smart contracts are self-executing contracts that allow for the automatic transfer of digital assets when certain conditions are met. In this series, we will cover some of the more tricky areas of Solidity, aimed at the Intermediate level of Solidity skills.

In Part 2 we covered Payable, Fallback, and Receive.

Inheritance is a powerful feature in Solidity that allows for code reuse and organization. It enables a contract to acquire properties and behavior (code) of a parent contract, by using the keyword is. In this article, we'll delve deeper into the nuances of multiple inheritance and its implications on the compiled code and bytecode.

Understanding Inheritance in Solidity

In Solidity, a contract can inherit from other contracts, which allows for polymorphism — a principle that enables one entity to take on many forms. In the context of Solidity, it means that a contract can be defined and then extended by other contracts.

Here’s a simple example:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract A {
function foo() public pure returns (string memory) {
return "foo";
}
}
contract B is A {
// B now has the function foo()
}

In this example, the contract B inherits from contract A, and therefore it has access to A's functions and variables. Contract B can be said to be a subtype of A.

Overriding Functions and Variables

In Solidity, a derived contract can override functions and variables of the base contract. To do this, the base function or variable must be marked as virtual, and the derived function or variable must be marked as override.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract A {
function foo() public pure virtual returns (string memory) {
return "foo";
}
}
contract B is A {
function foo() public pure override returns (string memory) {
return "foo in B";
}
}

In this example, the contract B overrides the foo function of the contract A. Therefore, if you call foo on an instance of, it will return "foo in B" instead of "foo".

Calling Parent Contract Functions

In Solidity, a derived contract can call a function from a base contract using the super keyword. This is useful when you want to extend the base contract's behavior, rather than completely replacing it.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract A {
function foo() public pure virtual returns (string memory) {
return "foo";
}
}
contract B is A {
function foo() public pure override returns (string memory) {
return string(abi.encodePacked("B -> ", super.foo()));
}
}

In this example, the contract B overrides the foo function of contract A, but it still calls A's foo function using the super keyword. Therefore, if you call foo on an instance of B, it will return "B -> foo".

Multiple Inheritance in Solidity

Solidity supports multiple inheritances. This means that a contract can inherit from multiple contracts. When a contract inherits from multiple contracts, it acquires the functions and variables of all parent contracts.

Here’s an example:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract A {
function foo() public pure returns (string memory) {
return "foo";
}
}
contract B {
function bar() public pure returns (string memory) {
return "bar";
}
}
contract C is A, B {
// C now has the function foo() from A and bar() from B
}

In this example, the contract C inherits from both A and B, and therefore it has access to A's foo function and B's bar function.

However, multiple inheritances can lead to a problem known as the “Diamond Problem”. This problem occurs when a contract inherits from two contracts that have a function with the same name and parameters. Solidity resolves this problem by using C3 Linearization to determine the order in which the parent contracts should be searched when looking for a function or variable.

Implications of Compiled Code and Bytecode

When a contract inherits from other contracts, the inherited contracts’ functionality is copied into the inheriting contract during the compilation process. This means that the bytecode of the inheriting contract includes the bytecode of all inherited contracts. This has several implications:

Code Reusability:

Inheritance allows for code reusability. You can define a contract and then extend it to other contracts. This helps to reduce code duplication and makes the code easier to manage.

Gas Costs:

The more functionality a contract has, the more bytecode it has, and the more gas it costs to deploy. Therefore, while inheritance can help to organize and reuse code, it can also increase the gas costs of deploying a contract.

Contract Size Limit:

Ethereum has a limit on the maximum size of a contract. If a contract inherits from many other contracts, or from contracts with a lot of code, it could exceed this limit. In this case, you would need to reduce the size of the contract by removing functionality or splitting it into multiple contracts.

Conclusion

Inheritance in Solidity allows for code reuse and organization, making it easier to manage larger codebases and create complex contracts. However, it’s important to be aware of the implications of inheritance on the compiled code and bytecode, particularly in terms of gas costs and the contract size limit. By understanding and using inheritance effectively, you can create robust and flexible smart contracts while managing the trade-offs.

In Part 4 we cover Transfer, Send and Call.

--

--

Shishir Singh

Digital Assets, Blockchains, DLTs, Tokenization & Protocols & AI Intersection